﻿using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;

namespace Z1029.NoteGlobalView
{
    public class ChartFormater : MonoBehaviour
    {
        [HideInInspector]
        public string ResultChart; 
        [HideInInspector]
        public JudgeLineSet judgeLineSet;
        [HideInInspector]
        public PhigrosV1Chart chart;
        public void BuildChart()
        {
            ResultChart = FormatChartV3();
            FileStream fs = new FileStream(Path.Combine(Path.GetDirectoryName(Application.dataPath), "Project//Chart.json"), System.IO.FileMode.Create, System.IO.FileAccess.Write);
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(ResultChart);
            fs.Write(bs, 0, bs.Length);
            fs.Close();
            print("OK!");
        }

        public static void Save()
        {
            string res = FormatChartV3();
            FileStream fs = new FileStream(Path.Combine(Path.GetDirectoryName(Application.dataPath), "Project//Chart.json"), System.IO.FileMode.Create, System.IO.FileAccess.Write);
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(res);
            fs.Write(bs, 0, bs.Length);
            fs.Close();
            print("OK!");
        }

        [Serializable]
        public class PhigrosV1Chart
        {
            public int formatVersion;
            public float offset;
            public int numOfNotes;
            public List<JudgeLineSet> judgeLineList;
        }

        [Serializable]
        public class PhigrosV3Chart
        {
            public int formatVersion;
            public float offset;
            public int numOfNotes;
            public List<JudgeLine> judgeLineList;
        }

        [Serializable]
        public class JudgeLineEvent : IComparable<JudgeLineEvent>
        {
            public float startTime;
            public float endTime;
            public float start;
            public float end;
            public float start2;
            public float end2;

            public int CompareTo(JudgeLineEvent other)
            {
                if (startTime > other.startTime)
                    return 1;
                if (startTime < other.startTime)
                    return -1;
                else return 0;
            }
        }

        [Serializable]
        public class JudgeLine
        {
            public int numOfNotes;
            public int numOfNotesAbove;
            public int numOfNotesBelow;
            public float bpm;
            public List<SpeedEventV3> speedEvents;
            public List<ChartNote> notesAbove;
            public List<ChartNote> notesBelow;
            public List<JudgeLineEvent> judgeLineDisappearEvents;
            public List<JudgeLineEvent> judgeLineMoveEvents;
            public List<JudgeLineEvent> judgeLineRotateEvents;
        }

        [Serializable]
        public class ChartNote : IComparable<ChartNote>
        {
            public int type;
            public int time;
            public float positionX;
            public float holdTime;
            public float speed;
            public float floorPosition;

            public int CompareTo(ChartNote other)
            {
                if (time > other.time)
                    return 1;
                if (time < other.time)
                    return -1;
                else return 0;
            }
        }

        [Serializable]
        public class SpeedEventV3 : IComparable<SpeedEventV3>
        {
            public float startTime;
            public float endTime;
            public float floorPosition;
            public float value;

            public int CompareTo(SpeedEventV3 other)
            {
                if (startTime > other.startTime)
                {
                    return 1;
                }
                else if (startTime < other.startTime)
                {
                    return -1;
                }
                else return 0;
            }
        }

        [Serializable]
        public class JudgeLineSet
        {
            public int numOfNotes;
            public int numOfNotesAbove;
            public int numOfNotesBelow;
            public float bpm;
            public List<SpeedEvent> speedEvents;
            public List<NoteSet> notesAbove;
            public List<NoteSet> notesBelow;
            public List<ValueS> judgeLineDisappearEvents;
            public List<ValueS> judgeLineMoveEvents;
            public List<ValueS> judgeLineRotateEvents;
        }

        [Serializable]
        public class ValueS:IComparable<ValueS>
        {
            public int startTime;
            public int endTime;
            public int start;
            public int end;

            public int CompareTo(ValueS other)
            {
                if (startTime > other.startTime)
                    return 1;
                if (startTime < other.startTime)
                    return -1;
                else return 0;
            }
        }

        [Serializable]
        public class NoteSet :IComparable<NoteSet>
        {
            public int type;
            public int time;
            public float positionX;
            public float holdTime;
            public float speed;
            public float floorPosition;

            public int CompareTo(NoteSet other)
            {
                if (time > other.time)
                    return 1;
                if (time < other.time)
                    return -1;
                else return 0;
            }
        }

        [Obsolete("请使用FormatChartV3()")]
        public static string FormatChartV1()
        {
            //chart;
            int nowTime = LevelController.instance.NowBeatTime;

            PhigrosV1Chart chart = new PhigrosV1Chart();
            LevelController ctl = LevelController.instance;
            int notesCount = 0;
            JudgementLine[] juds = new JudgementLine[FindObjectsOfType<JudgementLine>().Length];
            for(int i=0;i<juds.Length;i++)
            {
                juds[i] = LevelController.instance.m_level.GetChild(i).GetComponent<JudgementLine>();
            }
            chart.judgeLineList = new List<JudgeLineSet>();
            for (int i = 0; i < juds.Length; i++)
            {
                chart.judgeLineList.Add(new JudgeLineSet());
                JudgeLineSet jls = new JudgeLineSet();
                jls.judgeLineDisappearEvents = new List<ValueS>();
                jls.judgeLineMoveEvents = new List<ValueS>();
                jls.judgeLineRotateEvents = new List<ValueS>();
                jls.speedEvents = new List<SpeedEvent>();
                jls.notesAbove = new List<NoteSet>();
                jls.notesBelow = new List<NoteSet>();
                JudgementLine jl = juds[i];
                jls.bpm = jl.bpm;

                int j;
                for (j = 0; j < jl.alphaEvents.Count; j++)
                {
                    ValueS s = new ValueS();
                    s.start = Mathf.RoundToInt(jl.alphaEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.alphaEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].endTime, jl.bpm);
                    jls.judgeLineDisappearEvents.Add(s);
                }
                jls.judgeLineDisappearEvents.Sort();
                for (j = 0; j < jl.moveEvents.Count; j++)
                {
                    ValueS s = new ValueS();
                    s.start = Mathf.FloorToInt((jl.moveEvents[j].start + 8) * 1000 * 55 + (jl.moveEvents[j].start2 + 5) * 52);
                    s.end = Mathf.FloorToInt((jl.moveEvents[j].end + 8) * 1000 * 55 + (jl.moveEvents[j].end2 + 5) * 52);
                    s.start = s.start <= 0 ? 0 : s.start;
                    s.end = s.end <= 0 ? 0 : s.end;
                    s.startTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].endTime, jl.bpm);
                    jls.judgeLineMoveEvents.Add(s);
                }
                jls.judgeLineMoveEvents.Sort();
                for (j = 0; j < jl.rotateEvents.Count; j++)
                {
                    ValueS s = new ValueS();
                    s.start = Mathf.RoundToInt(jl.rotateEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.rotateEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].endTime, jl.bpm);
                    jls.judgeLineRotateEvents.Add(s);
                }
                jls.judgeLineRotateEvents.Sort();
                for (j = 0; j < jl.speeds.Length; j++)
                {
                    SpeedEvent s = new SpeedEvent();
                    s.startTime = Utils.GetBeatTimeFromTime(jl.speeds[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.speeds[j].endTime, jl.bpm);
                    s.value = jl.speeds[j].value;
                    jls.speedEvents.Add(s);
                }
                jls.speedEvents.Sort();
                
                Note[] ns = FindObjectsOfType<Note>();
                for (j = 0; j < ns.Length; j++)
                {
                    Note n = ns[j];
                    if (ns[j].judgeline == juds[i])
                    {
                        NoteSet s = new NoteSet();
                        Note.NoteData d = n.noteData;
                        s.time = d.beatTime;
                        s.speed = d.speed / 12;
                        s.holdTime = d.beatHoldTime;
                        s.positionX = d.positionX;
                        if (d.type == NoteType.Hold)
                            s.speed *= GetJudgelineSpeed((d.beatTime + d.beatHoldTime / 2), jls.speedEvents.ToArray());

                        //s.floorPosition = (float)d.time;
                        //LevelController.nowTime = d.time;
                        s.floorPosition = GetSpeedDistance((float)d.time,juds[i].speeds);
                        switch (d.type)
                        {
                            case NoteType.Click:
                                s.type = 1;
                                break;
                            case NoteType.Drag:
                                s.type = 2;
                                break;
                            case NoteType.Hold:
                                s.type = 3;
                                break;
                            case NoteType.Flick:
                                s.type = 4;
                                break;
                        }

                        if (n.direction == Note.Direction.Above)
                        {
                            jls.notesAbove.Add(s);
                        }
                        if (n.direction == Note.Direction.Below)
                        {
                            jls.notesBelow.Add(s);
                        }
                    }
                }
                jls.notesAbove.Sort();
                jls.notesBelow.Sort();
                jls.numOfNotesAbove = jls.notesAbove.Count;
                jls.numOfNotesBelow = jls.notesBelow.Count;
                jls.numOfNotes = jls.numOfNotesAbove + jls.numOfNotesBelow;
                notesCount += jls.numOfNotes;
                chart.judgeLineList[i] = jls;
            }
            chart.numOfNotes = notesCount;
            chart.offset = 0;
            chart.formatVersion = 1;
            return JsonUtility.ToJson(chart, true);
        }

        public static string FormatChartV3()
        {
            //chart;
            int nowTime = LevelController.instance.NowBeatTime;

            PhigrosV3Chart chart = new PhigrosV3Chart();
            LevelController ctl = LevelController.instance;
            int notesCount = 0;
            JudgementLine[] juds = new JudgementLine[FindObjectsOfType<JudgementLine>().Length];
            for (int i = 0; i < juds.Length; i++)
            {
                juds[i] = LevelController.instance.m_level.GetChild(i).GetComponent<JudgementLine>();
            }
            chart.judgeLineList = new List<JudgeLine>();
            for (int i = 0; i < juds.Length; i++)
            {
                chart.judgeLineList.Add(new JudgeLine());
                JudgeLine jls = new JudgeLine();
                jls.judgeLineDisappearEvents = new List<JudgeLineEvent>();
                jls.judgeLineMoveEvents = new List<JudgeLineEvent>();
                jls.judgeLineRotateEvents = new List<JudgeLineEvent>();
                jls.speedEvents = new List<SpeedEventV3>();
                jls.notesAbove = new List<ChartNote>();
                jls.notesBelow = new List<ChartNote>();
                JudgementLine jl = juds[i];
                jls.bpm = jl.bpm;

                int j;
                for (j = 0; j < jl.alphaEvents.Count; j++)
                {
                    JudgeLineEvent s = new JudgeLineEvent();
                    s.start = Mathf.RoundToInt(jl.alphaEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.alphaEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].endTime, jl.bpm);
                    jls.judgeLineDisappearEvents.Add(s);
                }
                jls.judgeLineDisappearEvents.Sort();
                for (j = 0; j < jl.moveEvents.Count; j++)
                {
                    JudgeLineEvent s = new JudgeLineEvent();
                    s.start = (jl.moveEvents[j].start / 18) +0.5f;
                    s.start2 = (jl.moveEvents[j].start2 /10) +0.5f;
                    s.end = (jl.moveEvents[j].end / 18) +0.5f;
                    s.end2 = (jl.moveEvents[j].end2 /10) +0.5f;
                    s.startTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].endTime, jl.bpm);
                    jls.judgeLineMoveEvents.Add(s);
                }
                jls.judgeLineMoveEvents.Sort();
                for (j = 0; j < jl.rotateEvents.Count; j++)
                {
                    JudgeLineEvent s = new JudgeLineEvent();
                    s.start = Mathf.RoundToInt(jl.rotateEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.rotateEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].endTime, jl.bpm);
                    jls.judgeLineRotateEvents.Add(s);
                }
                jls.judgeLineRotateEvents.Sort();
                for (j = 0; j < jl.speeds.Length; j++)
                {
                    jls.speedEvents.Sort();
                    SpeedEventV3 s = new SpeedEventV3();
                    s.startTime = Utils.GetBeatTimeFromTime(jl.speeds[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.speeds[j].endTime, jl.bpm);
                    try
                    {
                        s.floorPosition = jls.speedEvents[j - 1].floorPosition + (float)Utils.GetTimeFromBeatTime(jls.speedEvents[j - 1].endTime - jls.speedEvents[j - 1].startTime, jl.bpm) * jls.speedEvents[j - 1].value;
                    }
                    catch
                    {
                        s.floorPosition = 0;
                    }
                    s.value = jl.speeds[j].value;
                    jls.speedEvents.Add(s);
                }
                jls.speedEvents.Sort();

                Note[] ns = FindObjectsOfType<Note>();
                for (j = 0; j < ns.Length; j++)
                {
                    Note n = ns[j];
                    if (ns[j].judgeline == juds[i])
                    {
                        ChartNote s = new ChartNote();
                        Note.NoteData d = n.noteData;
                        s.time = d.beatTime;
                        s.speed = d.speed / 12;
                        s.holdTime = d.beatHoldTime;
                        s.positionX = d.positionX;
                        if (d.type == NoteType.Hold)
                            s.speed *= GetJudgelineSpeedV3((d.beatTime + d.beatHoldTime / 2), jls.speedEvents.ToArray());

                        //s.floorPosition = (float)d.time;
                        //LevelController.nowTime = d.time;
                        s.floorPosition = GetSpeedDistance((float)d.time, juds[i].speeds);
                        switch (d.type)
                        {
                            case NoteType.Click:
                                s.type = 1;
                                break;
                            case NoteType.Drag:
                                s.type = 2;
                                break;
                            case NoteType.Hold:
                                s.type = 3;
                                break;
                            case NoteType.Flick:
                                s.type = 4;
                                break;
                        }

                        if (n.direction == Note.Direction.Above)
                        {
                            jls.notesAbove.Add(s);
                        }
                        if (n.direction == Note.Direction.Below)
                        {
                            jls.notesBelow.Add(s);
                        }
                    }
                }
                jls.notesAbove.Sort();
                jls.notesBelow.Sort();
                jls.numOfNotesAbove = jls.notesAbove.Count;
                jls.numOfNotesBelow = jls.notesBelow.Count;
                jls.numOfNotes = jls.numOfNotesAbove + jls.numOfNotesBelow;
                notesCount += jls.numOfNotes;
                chart.judgeLineList[i] = jls;
            }
            chart.numOfNotes = notesCount;
            chart.offset = 0;
            chart.formatVersion = 3;
            return JsonUtility.ToJson(chart, true);
        }

        public static float GetSpeedDistance(float time,SpeedEvent[] speeds)
        {
            float num = 0;
            int index = speeds.Length - 1;
            for (int i = 0; i < speeds.Length; i++)
            {
                float start = speeds[i].startTime;
                float end = speeds[i].endTime;
                float value = speeds[i].value;
                if (start > time)
                {
                    index = i - 1;
                    break;
                }
                if (end > 0)
                {
                    if (start < 0)
                        num += (end - 0) * value;
                    else num += (end - start) * value;
                }
            }
            num -= (speeds[index].endTime - time) * speeds[index].value;
            return num;
        }

        public static float GetJudgelineSpeed(float time, SpeedEvent[] sp)
        {
            float result = 0;
            for (int i = 0; i < sp.Length; i++)
            {
                SpeedEvent sppp = sp[i];
                if (sppp.startTime < time && sppp.endTime > time) result = sppp.value;
            }
            return result;
        }

        public static float GetJudgelineSpeedV3(float time, SpeedEventV3[] sp)
        {
            float result = 0;
            for (int i = 0; i < sp.Length; i++)
            {
                SpeedEventV3 sppp = sp[i];
                if (sppp.startTime < time && sppp.endTime > time) result = sppp.value;
            }
            return result;
        }
    }
}